#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h> //struct sockaddr_in
#include <arpa/inet.h>  //inet_ntoa()  inet_addr()
#include <unistd.h>     //close()
#include <cstring>
using namespace std;
/*
流式域套接字利用udp实现进程通信，注意点都说过了
*/
// 服务端
int main(int argc, const char *argv[])
{
    // 1、创建用于连接的套接字文件描述符
    int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
    // 参数1：AF_UNIX表示使用的是ipv4的通信协议
    // 参数2：SOCK_STREAM表示使用的是tcp通信
    // 参数3：由于参数2指定了协议，参数3填0即可
    if (sfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("socket success sfd = %d\n", sfd); // 3
    // 判断套接字文件是否存在
    if (access("./unix", F_OK) == 0)
    {
        // 说明文件已经存在，需要将其进行删除操作
        if (unlink("./unix") == -1)
        {
            perror("unlink error");
            return -1;
        }
    }
    // 2、绑定套接字文件描述符
    // 2.1 填充要绑定的ip地址和端口号结构体
    struct sockaddr_un sun;
    sun.sun_family = AF_UNIX; // 通信域
    // sun.sun_path = "./unix";      //?不可以，因为字符串赋值需要使用 strcpy
    strcpy(sun.sun_path, "./unix");
    // 2.2 绑定工作
    // 参数1：要被绑定的套接字文件描述符
    // 参数2：要绑定的地址信息结构体，需要进行强制类型转换，防止警告
    // 参数3：参数2的大小
    if (bind(sfd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");

    // 3、启动监听
    // 参数1：要启动监听的文件描述符
    // 参数2：挂起队列的长度
    if (listen(sfd, 128) == -1)
    {
        perror("listen error");
        return -1;
    }
    printf("listen success\n");
    // 4、阻塞等待客户端的连接请求
    // 定义变量，用于接受客户端地址信息结构体
    struct sockaddr_un cun;          // 用于接收地址信息结构体的
    socklen_t socklen = sizeof(cun); // 用于接收地址信息的长度
    int newfd = accept(sfd, (struct sockaddr *)&cun, &socklen);
    // 参数1：服务器套接字文件描述符
    // 参数2：用于接收客户端地址信息结构体的容器，如果不接收，也可以填NULL
    // 参数3：接收参数2的大小，如果参数2为NULL，则参数3也是NULL
    if (newfd == -1)
    {
        perror("accept error");
        return -1;
    }
    printf("[%s]:已连接成功!!!!\n", cun.sun_path); // 输出套接字文件
    // 5、数据收发
    char rbuf[128] = ""; // 数据容器
    while (1)
    {
        // 清空容器中的内容
        bzero(rbuf, sizeof(rbuf));
        // 从套接字中读取消息
        int res = recv(newfd, rbuf, sizeof(rbuf), 0);
        if (res == 0)
        {
            printf("对端已经下线\n");
            break;
        }
        printf("[%s]:%s\n", cun.sun_path, rbuf);
        // 对收到的数据处理一下，回给客户端
        strcat(rbuf, "*_*");
        // 将消息发送给客户端
        if (send(newfd, rbuf, strlen(rbuf), 0) == -1)
        {
            perror("send error");
            return -1;
        }
        printf("发送成功\n");
    }
    // 6、关闭套接字
    close(newfd);
    close(sfd);

    std::cout << "Hello, World!" << std::endl;
    return 0;
}
/*--------------------------------------------------------------------------------*/
// 客户端
int main(int argc, const char *argv[])
{
    // 1、创建用于通信的客户端套接字文件描述符
    int cfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (cfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("socket success cfd = %d\n", cfd); // 3
    // 2、绑定ip地址和端口号(可选)    如果没有绑定，系统也不会自动绑定，会给的一个随机的文件
    // 判断套接字文件是否存在
    if (access("./linux", F_OK) == 0)
    {
        // 说明文件已经存在，需要将其进行删除操作
        if (unlink("./linux") == -1)
        {
            perror("unlink error");
            return -1;
        }
    }
    // 2.1 填充要绑定的地址信息结构体
    struct sockaddr_un cun;
    cun.sun_family = AF_UNIX;
    strcpy(cun.sun_path, "./linux");
    // 2.2 绑定工作
    if (bind(cfd, (struct sockaddr *)&cun, sizeof(cun)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");

    // 3、连接服务器
    // 3.1 填充要连接的服务器地址信息结构体
    struct sockaddr_un sun;
    sun.sun_family = AF_UNIX;       // 通信域
    strcpy(sun.sun_path, "./unix"); // 服务器套接字文件
    // 3.2 连接工作
    if (connect(cfd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
    {
        perror("connect error");
        return -1;
    }
    printf("连接服务器成功\n");

    // 4、数据收发
    char wbuf[128] = "";
    while (1)
    {
        // 清空容器
        bzero(wbuf, sizeof(wbuf));
        // 从终端获取数据
        fgets(wbuf, sizeof(wbuf), stdin); // 0
        wbuf[strlen(wbuf) - 1] = 0;       // 将换行改成 '\0'
        // 将数据发送给服务器
        if (send(cfd, wbuf, sizeof(wbuf), 0) == -1)
        {
            perror("send error");
            return -1;
        }
        // 接受服务器发送过来的消息
        if (recv(cfd, wbuf, sizeof(wbuf), 0) == 0)
        {
            printf("对端已经下线\n");
            break;
        }
        printf("收到服务器消息为：%s\n", wbuf);
    }
    // 5、关闭套接字
    close(cfd);
    std::cout << "Hello, World!" << std::endl;
    return 0;
}